package com.pc.admin.aop;

import com.pc.admin.annotation.Limiting;
import com.pc.admin.entity.FrequencyEntity;
import com.pc.admin.strategy.FrequencyStrategy;
import com.pc.admin.strategy.factory.FrequencyFactory;
import com.pc.admin.util.CommonResult;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
/**
 *  限流切面
 *  ps: 如使用Aop校验登录状态，使用ID限流需保证校验在限流切面前执行
 *
 * @author 吕飞扬
 * @date 2023/6/22 21:44
 */

@Aspect
@Component
@Slf4j
@Order(888)
public class LimitingAspect {
    @Resource
    RedisTemplate redisTemplate;

    @Pointcut("@annotation(limiting)")
    public void cut(Limiting limiting){
    }


    @Around("cut(limiting)")
    public Object limitimgAspect(ProceedingJoinPoint joinPoint, Limiting limiting) throws Throwable {
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

        FrequencyEntity entity = FrequencyFactory.getInstance().getFrequency(limiting, joinPoint);

        String key = entity.getKey();
        long nowTime = entity.getNowTime();
        long period = entity.getPeriod();
        int num = entity.getNum();
        try {
            //设置过期时间
            redisTemplate.expire(key,period,TimeUnit.SECONDS);

            //把限流时间之外的value删除
            zSetOperations.removeRangeByScore(key,0,nowTime-period*1000);
            //统计value(访问次数)
            Long count = zSetOperations.zCard(key);

            if(null != count && count+1 > num){

                String[] s = key.split("_");

                switch (limiting.target()){
                    case IP:
                        log.error("限流 ip:{},uri:{},method:{}.{}(),入参[k]:{},入参[v]:{}",s[s.length-2],s[s.length-1],joinPoint.getSignature().getDeclaringType(),
                                joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
                        break;

                    case EL:
                        log.error("限流 El:{},method:{}.{}(),入参[k]:{},入参[v]:{}",s[s.length-1],joinPoint.getSignature().getDeclaringType(),
                                joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
                        break;

                    default:
                        throw new IllegalStateException("Unexpected value: " + limiting.target());
                }

                return  CommonResult.sysError("访问频次受限，请稍后再试~");
            }
            log.info("放行 method:{}.{}(),入参[k]:{},入参[v]:{}",joinPoint.getSignature().getDeclaringType(),
                    joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
            return joinPoint.proceed();
        } finally {

            zSetOperations.add(key,nowTime,nowTime);
        }




    }


}
